package mylab.io.base.sync;

import com.google.common.base.Preconditions;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;

import java.util.Map;
import java.util.concurrent.TimeUnit;

import cn.hutool.core.map.MapUtil;
import mylab.io.base.model.Msg;
import mylab.io.base.model.Terminal;
import mylab.io.base.util.MsgUtil;


/**
 * 默认实现的同步配对器
 */
public class DefaultSynchro implements Synchro {

    /**
     * 【配对指令注册表】<K,V> 分别对应请求指令code与响应指令code
     */
    private final BiMap<String, String> registy = HashBiMap.create();

    /**
     * 按Terminal缓存请求指令，当响应指令可配对为同步指令时，从缓存移除
     */
    private CoupleCaches caches;


    //---------------------------------------------------  public -------------------------------------------------

    @Override
    public void regist(Map<String, String> pairs) {
        caches = new CoupleCaches();
        if (MapUtil.isNotEmpty(pairs)) {
            this.registy.putAll(pairs);
        }
    }

    @Override
    public void cacheRequest(Msg request) {
        Preconditions.checkState(MsgUtil.isValid(request));
        Preconditions.checkState(isRegisted(request), "unregisted request msg, code:" + request.getCode());

        CoupleCache cache = getCache(request.getTo());
        cache.cacheRequest(request);
    }

    @Override
    public boolean tryMatch(Msg response) {
        if (response == null || !isRegisted(response)) {
            return false;
        }

        String requestMsgCode = registy.inverse().get(response.getCode());
        if (requestMsgCode == null) {
            requestMsgCode = registy.get(response.getCode());
        }
        if (requestMsgCode == null) {
            return false;
        }

        Msg request = tryMatchRequest(response, requestMsgCode);


        boolean codeMatched = request != null;
        if (!codeMatched) {
            return false;
        }

        return customMatched(request, response);
    }

    @Override
    public Msg getResponse(Msg request, long timeout, TimeUnit unit) throws Exception {
        Preconditions.checkState(MsgUtil.isValid(request));
        Preconditions.checkState(isRegisted(request));

        CoupleCache cache = getCache(request.getTo());
        return cache.getResponse(request, timeout, unit);
    }

    @Override
    public void cleanCache(Msg request) {
        if (request != null) {
            CoupleCache cache = getCache(request.getTo());
            cache.cleanCache(request);
        }
    }

    @Override
    public void clear() {
        caches.getMap().forEach((terminal, coupleCache) -> coupleCache.clear());
        caches.clear();
    }

    //---------------------------------------------------  protected -------------------------------------------------

    /**
     * 自定义匹配规则
     */
    protected boolean customMatched(Msg request, Msg response) {
        return true;
    }


    protected Msg tryMatchRequest(Msg response, String requestMsgCode) {
        CoupleCache cache = getCache(response.getFrom());
        return cache.tryMatch(response, requestMsgCode);
    }

    protected boolean isRegisted(Msg msg) {
        return isRegisted(registy, msg);
    }

    protected boolean isRegisted(BiMap<String, String> map, Msg msg) {
        if (MapUtil.isNotEmpty(map) && msg != null) {
            return map.containsKey(msg.getCode()) || map.inverse().containsKey(msg.getCode());
        }
        return false;
    }

    protected CoupleCache getCache(Terminal terminal) {
        if (!caches.containsKey(terminal.getId())) {
            CoupleCache cache = new CoupleCache(terminal);
            caches.put(terminal.getId(), cache);
        }
        return caches.get(terminal.getId());
    }

}
